package com.erli.exchange.common.sg;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.erli.exchange.common.sg.entity.FieldInfo;

import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;

/**
 * 数据库表元数据
 * 
 * @title TableMetadataHelper.java
 * @package com.example.demo.sourcegenerate
 * @author lihu
 * @update 2019年3月6日 下午6:45:58
 */
public class TableMetadataHelper {

	private static Map<String, String> jdbcTypeToJavaTypeMap = new HashMap<String, String>();

	private static final String INFORMATION_SCHEMA_DATABASE = "information_schema";

	static {
		jdbcTypeToJavaTypeMap.put("CHAR", "String");
		jdbcTypeToJavaTypeMap.put("VARCHAR", "String");
		jdbcTypeToJavaTypeMap.put("LONGVARCHAR", "String");
		jdbcTypeToJavaTypeMap.put("NUMERIC", "BigDecimal");
		jdbcTypeToJavaTypeMap.put("DECIMAL", "BigDecimal");
		jdbcTypeToJavaTypeMap.put("BIT", "Boolean");
		jdbcTypeToJavaTypeMap.put("BOOLEAN", "Boolean");
		jdbcTypeToJavaTypeMap.put("TINYINT", "Byte");
		jdbcTypeToJavaTypeMap.put("SMALLINT", "Short");
		jdbcTypeToJavaTypeMap.put("INT", "Integer");
		jdbcTypeToJavaTypeMap.put("BIGINT", "Long");
		jdbcTypeToJavaTypeMap.put("FLOAT", "Double");
		jdbcTypeToJavaTypeMap.put("DOUBLE", "Double");
		jdbcTypeToJavaTypeMap.put("DATE", "LocalDateTime");
		jdbcTypeToJavaTypeMap.put("DATETIME", "LocalDateTime");
		jdbcTypeToJavaTypeMap.put("TIME", "LocalDateTime");
		jdbcTypeToJavaTypeMap.put("TIMESTAMP", "LocalDateTime");
	}

	/**
	 * 获取指定数据库下指定表名的所有字段信息
	 * 
	 * @param dataSource
	 * @param databaseName
	 * @param tableName
	 * @return
	 * @author lihu
	 * @update 2019年3月6日 下午6:48:07
	 */
	public static List<Entity> getColumnInfoByDataBaseNameAndTableName(DataSource dataSource,String databaseName, String tableName) {
		List<Entity> list = null;
		try {
			list = Db.use(dataSource)
					.findAll(Entity.create("COLUMNS").set("TABLE_SCHEMA", databaseName).set("TABLE_NAME", tableName));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * 构建DataSource
	 * 
	 * @param jdbcUrl
	 * @param user
	 * @param password
	 * @return
	 * @author lihu
	 * @update 2019年3月7日 下午2:01:26
	 */
	public static DataSource buildDataSource(String jdbcUrl, String user, String password) {
		if (!jdbcUrl.endsWith("/")) {
			jdbcUrl = jdbcUrl.concat("/");
		}
		DataSource ds = new SimpleDataSource(jdbcUrl.concat(INFORMATION_SCHEMA_DATABASE), user, password);
		return ds;
	}

	/**
	 * 查找指定数据库中指定表中的所有字段信息
	 * 
	 * @param dataSource
	 * @param databaseName
	 * @param tableName
	 * @return
	 * @author lihu
	 * @update 2019年3月7日 上午10:41:10
	 */
	public static List<FieldInfo> getFieldInfoByDataBaseNameAndTableName(DataSource dataSource, String databaseName,
			String tableName) {
		List<Entity> entityList = getColumnInfoByDataBaseNameAndTableName(dataSource, databaseName, tableName);
		if (entityList == null) {
			return null;
		}
		List<FieldInfo> result = new ArrayList<FieldInfo>(entityList.size());
		entityList.forEach(entity -> {
			// COLUMN_NAME, DATA_TYPE, COLUMN_KEY, COLUMN_COMMENT
			String columnField = entity.getStr("COLUMN_NAME");
			// 下划线转驼峰
			String propertyName = StrUtil.toCamelCase(columnField);
			String jdbcType = handleSpecialMysqlTypeToJdbcType(entity.getStr("DATA_TYPE"));
			String javaType = jdbcTypeToJavaType(jdbcType);
			String comment = entity.getStr("COLUMN_COMMENT");
			// 是否为主键ID
			boolean isPrimaryId = "PRI".equals(entity.getStr("COLUMN_KEY"));
			FieldInfo fieldInfo = new FieldInfo(columnField, propertyName, jdbcType, javaType, comment, isPrimaryId);
			result.add(fieldInfo);
		});
		return result;
	}

	/**
	 * JDBC类型转JAVA类型
	 * 
	 * @param jdbcType
	 * @return
	 * @author lihu
	 * @update 2019年3月7日 上午10:37:38
	 */
	public static String jdbcTypeToJavaType(String jdbcType) {
		jdbcType = jdbcType.toUpperCase();
		if (jdbcTypeToJavaTypeMap.containsKey(jdbcType)) {
			return jdbcTypeToJavaTypeMap.get(jdbcType);
		}
		throw new RuntimeException(String.format("JdbcType %s has no match to JavaType!", jdbcType));
	}

	/**
	   *   处理特殊的MySQL类型为JDBC类型
	 * 
	 * @param mysqlDataType
	 * @return
	 * @author lihu
	 * @update 2019年3月7日 上午10:37:38
	 */
	public static String handleSpecialMysqlTypeToJdbcType(String mysqlDataType) {
		String upperCaseDataType = mysqlDataType.toUpperCase();
		if (upperCaseDataType.equals("DATETIME")) {
			return "DATE";
		}
		return upperCaseDataType;
	}
}
